﻿/// <copyright file="ByteUtils.cs" company="not for commercial use">
/// Copyright (c) 6/4/2012 All Right Reserved
/// </copyright>
/// <author>Dimitry Kaplin</author>
/// <date>6/4/2012 21:50 </date>
/// <summary>Class containing methods for operations with bytes arrays</summary>

using System;
using System.Collections.Generic;
using System.Text;

namespace EthernetTools.Utils
{
    class ByteUtils
    {
        /// <summary>
        /// Delegate to log output function
        /// </summary>
        public static EthernetTools.Utils.Delegates.HandleLogDelegate LogWriteDelegate = null;
        
        /// <summary>
        /// Parsing bytes from string (ex. line "A0:23:FFzzz#@#$@#FE-1" return {0xA0,0x23,0xFF,0xFE,0x01})
        /// </summary>
        /// <param name="line">Line for parsing</param>
        /// <returns></returns>
        public static byte[] ParseBytes(string line, ByteParseStringMethod pMethod = ByteParseStringMethod.String2ByteConverting)
        {
            if (pMethod == ByteParseStringMethod.String2ByteConverting)
            {
                line = line.ToLower();
                List<byte> retVal = new List<byte>();
                char[] temp = new char[2];
                int j = 0;

                for (int i = 0; i < line.Length; ++i)
                {
                    if ((line[i] >= '0' && line[i] <= '9')
                        || (line[i] >= 'a' && line[i] <= 'f') || (line[i] >= 'A' && line[i] <= 'F'))
                    {
                        temp[j++] = line[i];
                    }

                    if (j >= 2)
                    {
                        try
                        {
                            retVal.Add(CharsToByte(temp[0], temp[1]));
                            j = 0;
                            continue;

                        }
                        catch (System.Exception ex)
                        {
                            if (LogWriteDelegate != null)
                                LogWriteDelegate("Error:Cannot parse line: \"" + line + "\" to bytes:" + Environment.NewLine + ex.Message, Options.Global.LogLevel.Error);
                            return null;
                        }
                    }
                }

                if (j != 0)
                    retVal.Add(CharsToByte('0', temp[0]));

                return retVal.ToArray();
            }
            else if (pMethod == ByteParseStringMethod.String2ByteDirectly)
            {
                byte[] temp = new byte[line.Length];
                int i = 0;
                foreach (char c in line)
                {
                    temp[i++] = (byte)c;
                }

                return temp;
            }
            else
                return null;
        }

        /// <summary>
        /// Appends bytes from src to dst. 
        /// </summary>
        /// <param name="Dst">Destination byte array</param>
        /// <param name="src">Source byte array</param>
        public static void AppendBytes(ref byte[] Dst, byte[] src)
        {
            // if nothing to append just return
            if (src == null)
                return;
            if (src.Length == 0)
                return;
            // if destination == null just copying
            if (Dst == null)
            {
                Dst = src;
                return;
            }
            if (Dst.Length == 0)
            {
                Dst = src;
                return;
            }
            int lastIndex = Dst.Length;

            byte[] newArr = new byte[Dst.Length + src.Length];

            for (int i = 0; i < Dst.Length; ++i)
            {
                newArr[i] = Dst[i];
            }

            for (int i = 0; i < src.Length; ++i)
            {
                newArr[i + lastIndex] = src[i];
            }

            Dst = newArr;
        }

        /// <summary>
        /// Converts TWO SYMBOLIC chars to byte (ex. 'A''0' return 0xA0)
        /// </summary>
        /// <param name="c1">first symbol</param>
        /// <param name="c2">second symbol</param>
        /// <returns>Valid byte if valid symbols present, 0x00 otherwise</returns>
        public static byte CharsToByte(char c1, char c2)
        {
            byte bh = 0x00;
            if (c1 >= '0' && c1 <= '9')
            {
                bh = (byte)(c1 - '0');
            }
            else if (c1 >= 'a' && c1 <= 'f')
            {
                bh = (byte)((c1 - 'a') + 0x0A);
            }
            else if (c1 >= 'A' && c1 <= 'F')
            {
                bh = (byte)((c1 - 'A') + 0x0A);
            }

            bh = (byte)(bh << 4);

            byte bl = 0x00;
            if (c2 >= '0' && c2 <= '9')
            {
                bl = (byte)(c2 - '0');
            }
            else if (c2 >= 'a' && c2 <= 'f')
            {
                bl = (byte)((c2 - 'a') + 0x0A);
            }
            else if (c2 >= 'A' && c2 <= 'F')
            {
                bl = (byte)((c2 - 'A') + 0x0A);
            }

            bh |= bl;
            return bh;
        }

        /// <summary>
        /// Converts byte array to string with delim (ex. 0xA0,0x23,0xFF return "A0:23:FF")
        /// </summary>
        /// <param name="arr">Byte array</param>
        /// <param name="delim">Delimetr between elements</param>
        /// <returns></returns>
        public static string ByteArrToString(byte[] arr, string delim = ":")
        {
            StringBuilder sb = new StringBuilder();
            if (arr != null)
            {
                for (int i = 0; i < arr.Length; ++i)
                {
                    byte b = arr[i];
                    sb.Append((char)((b / 0x10) >= 0x0a ? 'a' + ((b / 0x10) - 0xa) : '0' + (b / 0x10)));
                    sb.Append((char)((b % 0x10) >= 0x0a ? 'a' + ((b % 0x10) - 0xa) : '0' + (b % 0x10)));

                    if (i != arr.Length - 1)
                        sb.Append(delim);
                }
            }
            return sb.ToString();
        }

        /// <summary>
        /// Remove N bytes from array and change it
        /// </summary>
        /// <param name="count">Count of bytes which will be removed from source and returned</param>
        /// <param name="src">Source byte array</param>
        public static byte[] RemoveBytes(uint count, ref byte[] src)
        {
            byte[] retVal = null;
            // if nothing to append just return
            if (src == null)
                return retVal;
            if (count >= src.Length)
            {
                retVal = src;
                src = new byte[0];
                return retVal;
            }

            retVal = new byte[count];
            byte[] newSrc = new byte[src.Length - count];

            for (int i = 0, j = 0; i < src.Length; ++i)
            {
                if (i < retVal.Length)
                {
                    retVal[i] = src[i];
                }
                else
                {
                    newSrc[j++] = src[i];
                }
            }

            src = newSrc;

            return retVal;
        }

        /// <summary>
        /// Remove N bytes from array and change it
        /// </summary>
        /// <param name="count">Count of bytes which will be removed from source and returned</param>
        /// <param name="src">Source byte array</param>
        public static byte[] RemoveBytes(ulong count, ref byte[] src)
        {
            return RemoveBytes((uint)count, ref src);
        }

        /// <summary>
        /// Compare arrays by values
        /// </summary>
        /// <param name="count">A</param>
        /// <param name="src">B</param>
        public static bool Equals(byte[] a, byte[] b)
        {
            if (a == null && b == null)
                return true;
            
            if (a == null && b!= null)
                return false;

            if (a != null && b== null)
                return false;

            if (a.Length != b.Length)
                return false;

            for (int i = 0; i < a.Length; ++i)
            {
                if (a[i] != b[i])
                    return false;
            }

            return true;
        }
    }

    public enum ByteParseStringMethod
    {
        /// <summary>
        /// Directly translating text data to bytes (ex. "Hi" -> {(byte)'H', (byte)'i'})
        /// </summary>
        String2ByteDirectly,
        /// <summary>
        /// Input string expected to be an bytes representation (ex. "A0:23" -> {0xA0, 0x23}; "Hi" -> {null})
        /// </summary>
        String2ByteConverting,
    }

}
